package com.lemon.utils.wx;


import java.io.InputStream;
import java.security.AlgorithmParameters;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.lemon.conf.redis.RedisUtil;
import com.lemon.utils.QNFileUpload;
import com.lemon.utils.pay.Constants;
@SuppressWarnings("deprecation")
public class WeiXinUtils {

    private static final Logger logger = LoggerFactory.getLogger(WeiXinUtils.class);


 


    /**
     * 获取缔星工场小程序accToken
     *
     * @return
     */
    public static String getSmallProgramAuthAccessToken() {
        return getSmallProgramAuthAccessToken(Constants.WeChatAPPID, Constants.WeChatAPPsecret);
    } 

    /**
     * 获取小程序accToken
     * @param appid
     * @param secret
     * @return
     */
    public static synchronized String getSmallProgramAuthAccessToken(String appid, String secret) {


        String result = null;
        try {
            result = RedisUtil.get("auth_access_token_smallProgram" );
            if (!StringUtil.isEmpty(result)) {
                return result;
            }
            //缓存中没有,或已经失效
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
            JSONObject jsonObj = HTTPUtil.httpsRequest(url, "GET", null);
            logger.info("向小程序获取access_token,返回={}", jsonObj);

            result = jsonObj.getString("access_token");
            Integer expiresTime = Integer.parseInt(jsonObj.getString("expires_in"));
            //将baseAccessToken缓存
            RedisUtil.setex("auth_access_token_smallProgram", result, expiresTime - 1800);

            logger.info("程序获取获取access_token,返回={}", result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 为不同的小程序生成不同业务场景下的小程序二维码
     * * 注:目前调用的是微信可以生成带参数且无限个数的小程序码接口
     *
     * @param page
     * @param scene
     * @return
     */
    public static String getWxacode(String page, String scene) {
        return getWxacode(page, scene, 430, false);
    }
    
    
    public static void main(String[] args) {
		System.out.println(getWxacode("pages/giveDiscount/giveDiscount","78956235"));
	}

    /**
     * 为不同的小程序生成不同业务场景下的小程序二维码
     * 注:目前调用的是微信可以生成带参数且无限个数的小程序码接口
     * https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=accessToken
     *
     * @date: 2018年03月14日
     */
    public static String getWxacode(String page, String scene, int width, boolean isHyaline) {
        String imgUrl = "";
        try {
            String accessToken = getSmallProgramAuthAccessToken();
            if (accessToken == null) {
                return null;
            }
            String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;
            InputStream inputStream = sendRequest(url, page, scene, width, isHyaline);
			imgUrl = QNFileUpload.upload(inputStream , com.lemon.utils.StringUtil.getUUID() + ".jpg");
            inputStream.close();
        } catch (Exception e) {
            logger.error("请求getwxacodeunlimit接口获取小程序码异常.", e);
        }
		return QNFileUpload.QINIUPATH + imgUrl;
    }

    /**
     * 由于微信小程序码接口返回的是图片文件流,因此对该请求做单独封装以得到文件流进行后续处理
     *
     * @param url
     * @param page
     * @param scene
     * @return
     * @throws Exception
     */
    public static InputStream sendRequest(String url, String page, String scene) throws Exception {
        return sendRequest(url, page, scene, 330, false);
    }

    /**
     * 由于微信小程序码接口返回的是图片文件流,因此对该请求做单独封装以得到文件流进行后续处理
     *
     * @param url
     * @param page      必须是已经发布的小程序存在的页面（否则报错）
     * @param scene     最大32个可见字符，只支持数字，大小写英文以及部分特殊字符
     * @param width     二维码的宽度
     * @param isHyaline 是否需要透明底色， is_hyaline 为true时，生成透明底色的小程序码
     * @return
     * @throws Exception
     */
    @SuppressWarnings("resource")
    public static InputStream sendRequest(String url, String page, String scene, int width, boolean isHyaline) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        //设置请求头
        httpPost.setHeader("Content-type", "application/json");
        httpPost.addHeader("Accept", "application/json");
        //注意：此处的参数格式巨坑,目前找到解决方案是使用如下手动拼接JSON字符串的方式才可以调通微信的获取小程序码接口A或接口B,不然接口会报如下错误
        //{"errcode":47001,"errmsg":"data format error hint: [Z3jIja03054521]"} 大体意思就是参数格式错误
        //a.默认配置线条颜色 (以小程序头像背景色为基调)
        //String jsonStr = "{\"page\":\""+page+"\",\"width\":330,\"auto_color\":true,\"scene\":\""+scene+"\"}";
        //b.指定小程序码线条颜色 (黑色{"r":"0","g":"0","b":"0"})
        // String line_color = "\"line_color\":{\"r\":0,\"g\":0,\"b\":0}";
        // String jsonStr = "{\"page\":\"" + page + "\",\"width\":\"" + width + "\",\"auto_color\":false," + line_color + ",\"scene\":\"" + scene + "\"}";

        JSONObject obj = new JSONObject();
        obj.put("scene", scene);
        obj.put("page", page);
        obj.put("width", width);
        obj.put("auto_color", false);
        JSONObject line = new JSONObject();
        line.put("r", "0");
        line.put("g", "0");
        line.put("b", "0");
        obj.put("line_color", line);
        //obj.put("line_color", "{\"r\":0,\"g\":0,\"b\":0}");
        //是否需要透明底色， is_hyaline 为true时，生成透明底色的小程序码
        obj.put("is_hyaline", isHyaline);

        StringEntity param = new StringEntity(obj.toString(), "UTF-8");
        httpPost.setEntity(param);
        HttpClient httpclient = new DefaultHttpClient();
        HttpResponse response = httpclient.execute(httpPost);
        HttpEntity entity = response.getEntity();
       // System.out.println(EntityUtils.toString(entity, "utf-8"));
        //从响应实体中获得输入流
        InputStream inStream = entity.getContent();
        return inStream;
    }


    /**
     * 为自己做小程序时使用，获取微信用户对应某个小程序的session_key和openid等信息
     *
     * @param appid  小程序唯一标识
     * @param secret 小程序的 app secret
     * @param code   登录小程序时获取的 code
     * @return sessionKeyJson
     * @author hujia
     * @date 2018年5月15日  下午4:56:37
     */
    public static JSONObject getSmallRoutineSessionKey(String appid, String secret, String code) {
        String url = String.format("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appid, secret, code);
        logger.info("请求获取session_key的地址为：" + url);
        JSONObject sessionKeyJson = null;
        try {
            sessionKeyJson = HTTPUtil.httpsRequest(url, "POST", null);
            logger.info("请求微信登录凭证校验接口成功. [result={}]", sessionKeyJson);
        } catch (Exception e) {
            logger.error("请求微信登录凭证校验接口失败. [error={}]", e);
        }
        return sessionKeyJson;
    }


    public static String DecryptDataGetUserInfoJson(String encryptedData, String sessionkey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.getDecoder().decode(sessionkey);
        // 偏移量
        byte[] ivByte = Base64.getDecoder().decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取公众号js AccessToken
     * @param code
     * @return
     */
    public static JSONObject getUserInfoAccessToken (String code) {
        JSONObject json = null;
        try {
            String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
            		Constants.PUBLIC_ACCOUNT_APPID, Constants.PUBLIC_ACCOUNT_SECRET, code);
            logger.info("请求access_token，参数URL："+url);
            String tokens = sendHttpGetRequest(url);
            json = JSONObject.parseObject(tokens, JSONObject.class);
            logger.info("请求access_token成功. [result={}]", json);
        } catch (Exception e) {
            logger.error("请求access_token失败. [error={}]", e);
        }
        return json;
    }
    
    
    /**
     * 通过acctoken openid 获取用户信息
     * @param accessToken
     * @param openId
     * @return
     */
    public static JSONObject getUserInfo(String accessToken, String openId) {
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
        logger.info("请求用户信息，参数URL："+url);
        JSONObject userInfo = null;
        try {
            String tokens = sendHttpGetRequest(url);
            userInfo = JSONObject.parseObject(tokens, JSONObject.class);
            logger.info("请求用户信息成功. [result={}]", userInfo);
        } catch (Exception e) {
            logger.error("请求用户信息失败. [error={}]", e);
        }
        return userInfo;
    }
    
    
    
    /**
     * 向微信 亲贵获取公众号全局acctoken
     * @return
     * @throws Exception
     */
    public static String getBaseAccessToken(String appid,String appSecret) throws Exception{ 
 
		//缓存中没有,或已经失效
	    String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid+"&secret="+ appSecret;	    
	    String res = sendHttpGetRequest(url);
	    logger.info("向微信获取access_token,返回={}",res);
	    JSONObject jsonObj = JSONObject.parseObject(res);
	    String baseAccessToken = jsonObj.getString("access_token");
	  //  Integer expiresTime = Integer.parseInt(jsonObj.getString("expires_in"));
		    //将baseAccessToken缓存
		  //  jedis.set("HRM_WEIXIN_BASE_ACCESS_TOKEN", baseAccessToken, expiresTime-1800);
    	return baseAccessToken;
    }
    
    
    /**
     * 获取全局的jsapi_ticket
     * @return
     * @throws Exception
     */
    public static String getJsapiTicket() throws Exception{
		String key = "HRM_WEIXIN_JS_API_TICKET";
		String cache = RedisUtil.get(key);
		if(StringUtil.isNotEmpty(cache)) return cache;
		//缓存中没有,或已经失效
    	String baseAccessToken = getBaseAccessToken();
    	String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ baseAccessToken +"&type=jsapi";
    	String res = sendHttpGetRequest(url);
    	JSONObject jsonObj = JSONObject.parseObject(res);
    	String jsapiTicket = jsonObj.getString("ticket");
    	RedisUtil.setex(key, jsapiTicket, 60*60);
 
    	return jsapiTicket;
    }
   
    
    /**
     * 生成JS调用微信接口的签名参数
     * @param url当前页面的URL
     * @return
     * @throws Exception
     */
    public static JSONObject createSignature(String url) throws Exception{
    	String signature = "jsapi_ticket="+getJsapiTicket();
    	String noncestr = StringUtil.getUUID();
    	long timestamp = System.currentTimeMillis()/1000;
    	signature = signature+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
    	//根据jsapi_ticket等参数进行SHA1加密
    	signature = SHA1(signature);
    	JSONObject json = new JSONObject();
    	json.put("appid", Constants.PUBLIC_ACCOUNT_APPID);
    	json.put("noncestr", noncestr);
    	json.put("signature", signature.toLowerCase());
    	json.put("timestamp", timestamp);
    	return json;
    }
    
    /**
     * SHA1 加密算法
     * @param str
     * @return
     */
    public static String SHA1(String str) {
        try {
        	//如果是SHA加密只需要将"SHA-1"改成"SHA"即可
            MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1"); 
            digest.reset();
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexStr = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexStr.append(0);
                }
                hexStr.append(shaHex);
            }
            return hexStr.toString();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    
    public static String getBaseAccessToken() { 	 
		String baseAccessToken = null;
		String key = "h5_access_token";
		String cache = RedisUtil.get(key);
		if(StringUtil.isNotEmpty(cache)) return cache;
		try {
			baseAccessToken = getBaseAccessToken(Constants.PUBLIC_ACCOUNT_APPID,Constants.PUBLIC_ACCOUNT_SECRET);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		RedisUtil.setex(key, baseAccessToken, 60*60);
		return baseAccessToken;
    }
    
    
    
    
    @SuppressWarnings("resource")
  	public static String sendHttpGetRequest(String url) throws Exception{
      	HttpGet httpGet = new HttpGet(url);
          HttpClient httpclient = new DefaultHttpClient(); 
          HttpResponse response = httpclient.execute(httpGet);
          HttpEntity httpEntity = response.getEntity();
          String result = EntityUtils.toString(httpEntity, "utf-8");
          return result;
      }

}
